package com.sunlands.zlcx.usercenter.service;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.sunlands.zlcx.usercenter.client.DeskMatePushService;
import com.sunlands.zlcx.usercenter.client.bean.PushDTO;
import com.sunlands.zlcx.usercenter.constant.LearningStatus;
import com.sunlands.zlcx.usercenter.domain.LearnContentDO;
import com.sunlands.zlcx.usercenter.repository.LearnContentRepository;
import com.sunlands.zlcx.usercenter.util.BeanPropertiesUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import static com.sunlands.zlcx.usercenter.config.RedisConfig.CacheKeys.*;
import static com.sunlands.zlcx.usercenter.constant.LearningStatus.LEARN_TIMING;
import static com.sunlands.zlcx.usercenter.constant.LearningStatus.LEARN_TIMING_CONINUE;
import static com.sunlands.zlcx.usercenter.constant.LearningStatus.LEARN_TIMING_PAUSE;

/**
 * @author shixiaopeng
 */
@Service
@Slf4j
public class UsersLearnContentServiceImpl implements BeanPropertiesUtil {

    @Autowired
    private LearnContentRepository learnContentRepository;
    @Autowired
    private UsersStudyServiceImpl usersStudyService;
    @Autowired
    private DeskMatePushService deskMatePushService;

    @Caching(evict = {
            @CacheEvict(cacheNames = USERS_LEARN_CONTENT_LIST, key = "#learnContentDO.userId"),
            @CacheEvict(cacheNames = USERS_LEARN_ALL_CONTENT_LIST, key = "#learnContentDO.userId"),
            @CacheEvict(cacheNames = USERS_LEARN_CONTENT_BY_ID, key = "#learnContentDO.id")
    })
    public LearnContentDO save(LearnContentDO learnContentDO) {
        return learnContentRepository.save(learnContentDO);
    }

    @Caching(evict = {
            @CacheEvict(cacheNames = USERS_LEARN_CONTENT_LIST, key = "#learnContentDO.userId"),
            @CacheEvict(cacheNames = USERS_LEARN_ALL_CONTENT_LIST, key = "#learnContentDO.userId"),
            @CacheEvict(cacheNames = USERS_LEARN_CONTENT_BY_ID, key = "#learnContentDO.id")
    })
    public LearnContentDO update(LearnContentDO learnContentDO) {
        return learnContentRepository.save(learnContentDO);
    }

    @Cacheable(cacheNames = USERS_LEARN_CONTENT_LIST, key = "#userId", unless = "#result == null")
    public List<LearnContentDO> findByUserId(Long userId) {
        return learnContentRepository.findAllByUserIdAndIsDeleteOrderByStatusDescUpdateTimeDesc(userId, LearnContentDO.DeleteEnum.NO.code);
    }

    @Cacheable(cacheNames = USERS_LEARN_ALL_CONTENT_LIST, key = "#userId", unless = "#result == null")
    public List<LearnContentDO> findAllByUserId(Long userId) {
        return learnContentRepository.findAllByUserIdOrderByStatusDescUpdateTimeDesc(userId);
    }

    @Cacheable(cacheNames = USERS_LEARN_CONTENT_BY_ID, key = "#id", unless = "#result == null")
    public LearnContentDO findOne(Long id) {
        return learnContentRepository.findOne(id);
    }

    public LearnContentDO findByContent(Long userId, String content) {
        return learnContentRepository.findFirstByUserIdAndContentOrderByIdDesc(userId, content);
    }

    public Long count(Long userId) {
        return learnContentRepository.countByUserIdAndIsDelete(userId, LearnContentDO.DeleteEnum.NO.code);
    }

    @Transactional
    public void updateContent(Long userId, String oldContent, String newContent) {
        learnContentRepository.updateContent(userId, oldContent, newContent);
    }

    public LearnContentDO findLearning(Long userId) {
        List<Integer> statusList = new ArrayList<>();
        statusList.add(LEARN_TIMING.getCode());
        statusList.add(LEARN_TIMING_CONINUE.getCode());
        statusList.add(LEARN_TIMING_PAUSE.getCode());
        return learnContentRepository.findByUserIdAndStatusIn(userId, statusList);
    }

    public LearnContentDO findLearningAndModelValid(Long userId) {
        List<Integer> statusList = new ArrayList<>();
        statusList.add(LEARN_TIMING.getCode());
        statusList.add(LEARN_TIMING_CONINUE.getCode());
        return learnContentRepository.findByUserIdAndModelAndStatusIn(userId, LearnContentDO.ModelEnum.valid.code, statusList);
    }

    public static ThreadFactory threadFactoryName = new ThreadFactoryBuilder()
            .setNameFormat("push-leave-learning-pool-%d").build();
    ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(1, threadFactoryName);

    //TODO  继续计时，也需要调用
    public void schedeledTask(LearnContentDO contentDO, Long seconds) {
        scheduledExecutorService.schedule(() -> learnPush(contentDO.getUserId(), contentDO.getId()), seconds, TimeUnit.SECONDS);
    }

    /**
     * 倒计时剩余五分钟推送
     */
    public void learnPush(Long userId, Long contentId) {
        LearnContentDO contentDO = findOne(contentId);
        Integer model = contentDO.getModel();
        if (LearnContentDO.ModelEnum.invalid.code.intValue() == model) {
            //正计时，忽略
            return;
        }
        Integer status = contentDO.getStatus();
        if (LearningStatus.LEARN_TIMING.getCode() == status || LearningStatus.LEARN_TIMING_CONINUE.getCode() == status) {
            JSONObject jsonObject = usersStudyService.getLearnTimeByUserId(userId);
            //已学习时长，秒
            Long learnTime = jsonObject.getLong("learnTime");

            Integer minutes = contentDO.getMinutes();

            if (Math.abs(minutes * 60 - learnTime - 5 * 60) < 60) {
                log.info("小哥哥要噗==push ");
                PushDTO pushDTO = PushDTO.builder().content("本次专注时间还有5分钟结束！").type(1).ids(Lists.newArrayList(userId + "")).build();
                deskMatePushService.push(pushDTO);
            } else if (minutes * 60 - learnTime > 5 * 60) {
                //差距超过一分钟了
                schedeledTask(contentDO, minutes * 60 - learnTime - 5 * 60);
            }
        }
    }
}
